//-------------------------------------------------------------------------------------------------
// <copyright file="MainForm.cs" company="OfficeClip LLC">
// Copyright (c) OfficeClip LLC.  All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------

namespace OfficeClip.OpenSource.StyleFix
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.IO;
    using System.Security.AccessControl;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;
    using StyleCop;
    using NArrange.Core;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Reflection;
    using System.Diagnostics;

    /// <summary>
    /// The main form
    /// </summary>
    public partial class MainForm : Form
    {
        private Configure configure;
        private string filePath;
        private Thread thread;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        public MainForm()
        {
            this.InitializeComponent();
            if (!string.IsNullOrEmpty(this.txtFolder.Text = Properties.Settings.Default.Project))
            {
                this.PopulateFileListBox();
            }
            //isNArrange = Properties.Settings.Default.NArrange;
            //isAStyle = Properties.Settings.Default.AStyle;
            this.configure = new Configure();
            filePath = string.Empty;

            // Let's have the window title change based on the version entered in the AssemblyInfo file.
            this.Text = Application.ProductName + " " + Assembly.GetExecutingAssembly().GetName().Version.ToString();
        }

        private bool IsNArrange
        {
            get
            {
                return Properties.Settings.Default.NArrange;
            }
        }

        private bool IsAStyle
        {
            get
            {
                return Properties.Settings.Default.AStyle;
            }
        }

        private void Button1_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private Dictionary<String, String> Projects;

        private void BtnBrowse_Click(object sender, EventArgs e)
        {
            this.openFileDialog1.Filter = "C# Projects|*.csproj|Solution|*.sln";
            System.Windows.Forms.DialogResult result = this.openFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                this.txtFolder.Text = Properties.Settings.Default.Project = this.openFileDialog1.FileName;
                Properties.Settings.Default.Save();
                this.PopulateFileListBox();
            }
        }

        private void PopulateFileListBox()
        {
            this.filesListBox.Items.Clear();
            if (!File.Exists(this.txtFolder.Text))
            {
                return;
            }
            string filename = this.txtFolder.Text;
            string extension = Path.GetExtension(filename);
            Projects = new Dictionary<string, string>();
            if (extension == ".sln")
            {
                foreach (var project in GetProjects(filename))
                {
                    foreach (string fname in this.GetFiles(new DirectoryInfo(Path.GetDirectoryName(project))).Where(f => !Excluded(f)).ToList())
                    {
                        Projects.Add(fname, project);
                    }
                }
            }
            else if (extension == ".csproj")
            {
                foreach (string fname in this.GetFiles(new DirectoryInfo(Path.GetDirectoryName(this.txtFolder.Text))).Where(f => !Excluded(f)).ToList())
                {
                    Projects.Add(fname, filename);
                }
            }

            this.filesListBox.Items.AddRange(Projects.Select(kvp => kvp.Key).ToArray());
        }

        private bool Excluded(string f)
        {
            return f.EndsWith("Designer.cs") || f.Contains("TemporaryGeneratedFile") || f.EndsWith(".g.i.cs") || f.EndsWith(".g.cs");
        }

        private IEnumerable<string> GetProjects(string SlnPath)
        {
            var Content = File.ReadAllText(SlnPath);
            Regex projReg = new Regex(
                @"Project\(""\{[\w-]*\}""\)\s?=\s?""([\w _\.]*)"",\s?""(.*\.(csproj))"""
                , RegexOptions.Compiled);
            var matches = projReg.Matches(Content).Cast<Match>();
            var Projects = matches.Select(x => x.Groups[2].Value).ToList();
            for (int i = 0; i < Projects.Count; ++i)
            {
                if (!Path.IsPathRooted(Projects[i]))
                {
                    Projects[i] = Path.Combine(Path.GetDirectoryName(SlnPath), Projects[i]);
                }
                yield return Path.GetFullPath(Projects[i]);
            }
        }

        private void CheckListBox(CheckedListBox checkListBox, bool isChecked)
        {
            for (int i = 0; i < checkListBox.Items.Count; i++)
            {
                if (checkListBox.GetItemCheckState(i) != CheckState.Indeterminate)
                {
                    checkListBox.SetItemChecked(i, isChecked);
                }
            }
        }

        private List<string> GetFiles(DirectoryInfo dir)
        {
            List<string> fileList = new List<string>();
            foreach (FileInfo fi in dir.GetFiles("*.cs"))
            {
                fileList.Add(fi.FullName);
            }

            foreach (DirectoryInfo di in dir.GetDirectories())
            {
                foreach (string fileStr in this.GetFiles(di))
                {
                    fileList.Add(fileStr);
                }
            }

            return fileList;
        }

        private void ProcessingUI(bool IsEnable)
        {
            this.Invoke(new MethodInvoker(() =>
                                              {
                                                  btnBrowse.Enabled = IsEnable;
                                                  btnConfigure.Enabled = IsEnable;
                                                  btnProcess.Enabled = IsEnable;
                                                  button1.Enabled = IsEnable;
                                              }));
            //btnBrowse.Enabled = IsEnable;
            //btnConfigure.Enabled = IsEnable;
            //btnProcess.Enabled = IsEnable;
            //button1.Enabled = IsEnable;
        }

        private void WriteInOutput(string output)
        {
            this.Invoke(new MethodInvoker(() =>
                                              {
                                                  txtOutput.Text += output;
                                                  txtOutput.SelectionStart = txtOutput.TextLength;
                                                  txtOutput.ScrollToCaret();
                                              }));
        }

        private void UpdateProgress(int progress)
        {
            this.Invoke(new MethodInvoker(() =>
            {
                progressBar1.Value = progress;
            }));
        }

        private void UpdateViolationsCount(int violations)
        {
            this.Invoke(new MethodInvoker(() =>
            {
                label5.Text = violations.ToString();
            }));
        }

        private void UpdateFixedCount(int violations)
        {
            this.Invoke(new MethodInvoker(() =>
            {
                label7.Text = violations.ToString();
            }));
        }

        private void BackUpFile(string filePath)
        {
            if (chkBackup.Checked)
            {
                string backupFilePath = string.Format(
                    @"{0}\{1}.orig{2}",
                    Path.GetDirectoryName(filePath),
                    Path.GetFileNameWithoutExtension(filePath),
                    Path.GetExtension(filePath));
                File.Copy(filePath, backupFilePath, true);
            }
        }

        private void BtnProcess_Click(object sender, EventArgs e)
        {
            // Check to see if they have any files for us to parse.
            if (this.filesListBox.CheckedItems.Count > 0)
            {
                ViolationFileName = Path.ChangeExtension(Path.GetTempFileName(), ".txt");
                thread = new Thread(() =>
                                    {
                                        ProcessingUI(false);

                                        int totalCorrected = 0;
                                        List<SAObject> Violations = new List<SAObject>();

                                        StreamWriter voilationWriter = File.CreateText(ViolationFileName);
                                        for (int i = 0; i < this.filesListBox.Items.Count; i++)
                                        {
                                            if (this.filesListBox.GetItemChecked(i))
                                            {
                                                filePath = filesListBox.Items[i].ToString();
                                                BackUpFile(filePath);
                                                WriteInOutput(string.Format("{0}: StyleFix", Path.GetFileName(filePath)));
                                                string projectPath = Projects[filePath];
                                                StyleFix sf = new StyleFix(projectPath, filePath);
                                                voilationWriter.Write(StyleFix.ViolationContent);
                                                Violations.AddRange(sf.Violations);
                                                UpdateViolationsCount(Violations.Count);
                                                // Now we will run this through the NArrange library
                                                if (IsNArrange)
                                                {
                                                    WriteInOutput(", NArrange");
                                                    RunNArrange(filePath, filePath);
                                                }
                                                // Run the astyle
                                                if (IsAStyle)
                                                {
                                                    WriteInOutput(", AStyle");
                                                    RunAStyle(filePath);
                                                }

                                                WriteInOutput(System.Environment.NewLine);
                                                int total;
                                                int corrected;
                                                sf.CalculateViolations(out total, out corrected);
                                                totalCorrected += corrected;
                                                UpdateFixedCount(totalCorrected);
                                                WriteInOutput(
                                                    string.Format("*** Fixed: {0} violations out of {1} ***", corrected, total));
                                                WriteInOutput(System.Environment.NewLine);
                                            }
                                            UpdateProgress((i * 100) / this.filesListBox.Items.Count);
                                        }

                                        voilationWriter.Close();
                                        UpdateProgress(100);
                                        ProcessingUI(true);
                                        WriteInOutput("*** Done ***");
                                        WriteInOutput(System.Environment.NewLine);
                                        Dictionary<String, int> VAnalysis = new Dictionary<string, int>();
                                        Dictionary<String, int> VFixed = new Dictionary<string, int>();
                                        foreach (SAObject violation in Violations)
                                        {
                                            if (VFixed.ContainsKey(violation.ErrorId))
                                            {
                                                if (violation.IsFixed)
                                                    VFixed[violation.ErrorId]++;
                                            }
                                            else
                                            {
                                                if (violation.IsFixed)
                                                    VFixed.Add(violation.ErrorId, 1);
                                            }
                                            if (VAnalysis.ContainsKey(violation.ErrorId))
                                            {
                                                VAnalysis[violation.ErrorId]++;
                                            }
                                            else
                                            {
                                                VAnalysis.Add(violation.ErrorId, 1);
                                            }
                                        }
                                        foreach (var Pair in VAnalysis.OrderBy(kvp => kvp.Value))
                                            WriteInOutput(String.Format("{0}({1}/{2})\r\n", Pair.Key, VFixed.ContainsKey(Pair.Key) ? VFixed[Pair.Key] : 0, Pair.Value));
                                        WriteInOutput(string.Format("*** Fixed: {0} violations out of {1} ***", totalCorrected, Violations.Count));
                                        Complete();
                                    });
                thread.Start();
            }
            else
            {
                MessageBox.Show("Please select at least one file.", "Nothing to do", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void Complete()
        {
            this.Invoke(new MethodInvoker(() =>
            {
                if (MessageBox.Show("View violations?", "Process Complete", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                {
                    Process.Start(ViolationFileName);
                }
            }));
        }

        private void RunNArrange(string filePath, string newFilePath)
        {
            ConsoleLogger logger = new ConsoleLogger();
            Console.WriteLine();
            Console.WriteLine(new string('_', 60));
            FileArranger fileArranger = new FileArranger(
                string.Format(@"{0}\NArrangeConfig.xml", (new Utility()).GetSetupDir()),
                logger);
            bool success = fileArranger.Arrange(filePath, newFilePath, false);
            if (!success)
            {
                WriteInOutput(System.Environment.NewLine);
                WriteInOutput("Unable to NArrange");
                WriteInOutput(System.Environment.NewLine);
                logger.LogMessage(LogLevel.Error, "Unable to NArrange {0}.", filePath);
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            if (thread != null && thread.IsAlive)
            {
                thread.Abort();
            }
        }

        private void RunAStyle(string filePath)
        {
            Utility util = new Utility();
            string error = util.Run(
                util.GetSetupDir(),
                "AStyle.exe",
                string.Format(@"--options=""{0}\astyle.cfg""", util.GetSetupDir()),
                filePath);
            if (error != string.Empty)
            {
                WriteInOutput(error);
            }
        }

        private void chkSelectAll_CheckedChanged(object sender, EventArgs e)
        {
            this.CheckListBox(this.filesListBox, this.chkSelectAll.Checked);
        }

        private void btnConfigure_Click(object sender, EventArgs e)
        {
            this.configure.ShowDialog();
        }

        private void filesListBox_ItemCheck(object sender, ItemCheckEventArgs e)
        {
        }

        private void label5_Click(object sender, EventArgs e)
        {

        }

        private void label4_Click(object sender, EventArgs e)
        {

        }

        public string ViolationFileName { get; set; }
    }
}
